// ----------------
// Project:
// ESA Elevator
// ----------------
//
// Description:
// ----------------
// keyboard_if.v testbench
//
// Version History:
// ----------------
// 140116: set CLEAR_FLOOR_BUTTON_VALID = 0 afterwards, so every button can be delighted

`timescale 1ns / 1ps

module keyboard_if_tb_public;

  //****************** SIMULATION PARAMETERS *******************//  
  localparam CLK_PERIOD  =  50; // [ns] -> 20 MHz
  localparam NUM_FLOORS  =  99; // number of floors
  //***********************************************************//
  

  //*************** DERIVED SIMULATION PARAMETERS *************// 
  localparam FLOOR_BITS            = min_1(ceil_log2(NUM_FLOORS-1));
  //***********************************************************//
  

  //********************* MODULE INPUTS ***********************// 
  reg                          CLK;
  reg                          RESET;
  reg [(NUM_FLOORS-1)      :0] FLOOR_SELECT;             // 
  reg                          CLOSE_DOOR;               // 
  reg                          OPEN_DOOR;               // 
  reg                          PASSENGER_ALARM;          // 
  reg [(FLOOR_BITS-1)      :0] CLEAR_FLOOR_BUTTON;       // reset_button
  reg                          CLEAR_FLOOR_BUTTON_VALID; 
  //***********************************************************//  


  //********************* MODULE OUTPUTS **********************//
  wire                           CLOSE_DOOR_REQUEST;       // close
  wire                           OPEN_DOOR_REQUEST;        // open
  wire                           PASSENGER_ALARM_REQUEST;  // alarm        
  wire  [(NUM_FLOORS-1)      :0] SELECTED_FLOORS;          // floor
  wire  [(NUM_FLOORS-1)      :0] ENLIGHT_BUTTONS;          // enlight 
  //***********************************************************// 
  

  //******************* UUT INSTANTIATION *********************// 
  keyboard_if #(
               .FLOORS     (NUM_FLOORS),
               .FLOOR_BITS (FLOOR_BITS))
               
      KEYBOARD_IF_i (
                    .CLK(CLK),
                    .RESET(RESET),
                    .FLOOR_SELECT(FLOOR_SELECT),
                    .CLOSE_DOOR_IN(CLOSE_DOOR),
						  .OPEN_DOOR_IN(OPEN_DOOR),
                    .PASSENGER_ALARM_IN(PASSENGER_ALARM),
                    .CLEAR_FLOOR_BUTTON(CLEAR_FLOOR_BUTTON),
                    .CLEAR_FLOOR_BUTTON_VALID(CLEAR_FLOOR_BUTTON_VALID),
                    .CLOSE_DOOR_OUT(CLOSE_DOOR_REQUEST),
						  .OPEN_DOOR_OUT(OPEN_DOOR_REQUEST),
                    .PASSENGER_ALARM_OUT(PASSENGER_ALARM_REQUEST),
                    .SELECTED_FLOORS(SELECTED_FLOORS),
                    .ENLIGHT_BUTTONS(ENLIGHT_BUTTONS));                                
  //***********************************************************//
  
  
  //***************** SIMULATION VARIABLES ********************// 
  integer i;              
  reg  [(NUM_FLOORS-1)      :0] selected_floor;          // floor
  reg  [(NUM_FLOORS-1)      :0] enlight_button;          // enlight       
  //***********************************************************//
  

  //******************* 20 MHz CLOCK SIGNAL *******************//   
  always begin 
    #(CLK_PERIOD/2) CLK = ~CLK;
  end
  //***********************************************************// 


  //********************* TEST INITIATION *********************//  
  initial begin
    selected_floor           = 0;
    enlight_button           = 0;
    // Initialize Inputs
    CLK                      = 0;
    RESET                    = 1;
    FLOOR_SELECT             = 0;
    CLOSE_DOOR               = 0;
    OPEN_DOOR                = 0;
    PASSENGER_ALARM          = 0;
    CLEAR_FLOOR_BUTTON       = 0;
    CLEAR_FLOOR_BUTTON_VALID = 0;

    // Wait 125 ns for global reset to finish
    #(3*CLK_PERIOD/2);
    RESET = 0;
    $display("************ STARTING SIMULATION ************");  
    #(5*CLK_PERIOD);
    //--------------------------
    // SELECT FLOORS 
    //--------------------------
    FLOOR_SELECT    = 1 << 0;
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 1;
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 2;
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 3;
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 4;
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 5;  
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 80; 
    #(CLK_PERIOD);
    FLOOR_SELECT    = 1 << 45;           
    #(5*CLK_PERIOD);
    FLOOR_SELECT    = 0;           
    #(5*CLK_PERIOD);    
    //--------------------------
    // SELECT FLOORS 
    //--------------------------
    CLEAR_FLOOR_BUTTON       = 5;
    CLEAR_FLOOR_BUTTON_VALID = 1;         
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 4;
    CLEAR_FLOOR_BUTTON_VALID = 1;      
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 3;
    CLEAR_FLOOR_BUTTON_VALID = 1;      
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 2;
    CLEAR_FLOOR_BUTTON_VALID = 1;      
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 1;
    CLEAR_FLOOR_BUTTON_VALID = 1;   
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 0;
    CLEAR_FLOOR_BUTTON_VALID = 1;    
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 80;
    CLEAR_FLOOR_BUTTON_VALID = 1;    
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON       = 45;
    CLEAR_FLOOR_BUTTON_VALID = 1;  
    #(CLK_PERIOD);  
    CLEAR_FLOOR_BUTTON_VALID = 0;          
    #(5*CLK_PERIOD);  
    //--------------------------
    // CLOSE DOOR & PASSENGER ALRAM
    //--------------------------
    CLOSE_DOOR      = 1;
    #(CLK_PERIOD);  
    CLOSE_DOOR      = 0;
    PASSENGER_ALARM = 1;
    #(CLK_PERIOD);  
    PASSENGER_ALARM = 0;
    //--------------------------
    #(5*CLK_PERIOD);  
    $display("************ SIMULATION COMPLETE ************");
    $finish;
    //-------------------------- 
  end
  //***********************************************************// 
initial begin
  #(100000*CLK_PERIOD)
  $display("************ SIMULATION KILLED BECAUSE OF ERROR ************");
  $finish;
end


  //****************** VISUALISATION PROCESS ******************//
  always@(SELECTED_FLOORS) begin
    for(i=0;i<NUM_FLOORS;i=i+1) begin
      if(SELECTED_FLOORS[i] && ~selected_floor[i]) begin
        selected_floor[i] = 1'b1;
        $display("floor button %d selected", i);
      end
      else if(~SELECTED_FLOORS[i] && selected_floor[i]) begin
        selected_floor[i] = 1'b0;
        $display("floor button %d deselected", i);
      end
    end     
  end
  
  
  always@(ENLIGHT_BUTTONS) begin
    for(i=0;i<NUM_FLOORS;i=i+1) begin
      if(ENLIGHT_BUTTONS[i] && ~enlight_button[i]) begin
        enlight_button[i] = 1'b1;
        $display("floor button %d enlighted", i);
      end
      else if(~ENLIGHT_BUTTONS[i] && enlight_button[i]) begin
        enlight_button[i] = 1'b0;
        $display("floor button %d light off", i);
      end
    end     
  end
  
  always@(CLOSE_DOOR_REQUEST) begin
    if (CLOSE_DOOR_REQUEST)
        $display("received close door command");      
  end
  
  always@(PASSENGER_ALARM_REQUEST) begin
    if (PASSENGER_ALARM_REQUEST)
        $display("received passenger alarm");      
  end
  //***********************************************************// 


  //******************* PARAMETER FUNCTIONS *******************//
  //ceil of the log base 2
  function integer ceil_log2;
    input [31:0] value;
    for (ceil_log2=0; value>0; ceil_log2=ceil_log2+1)
      value = value>>1;
  endfunction
  
  // value cannot be less than 1
  function integer min_1;
    input [31:0] value;
    if (value == 0)
      min_1 = 1;
    else
      min_1 = value;
  endfunction 
  //***********************************************************//     
        
endmodule

